#pragma once

#include <vector>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>

namespace scNet {

	/**
	 * @brief 线程池类，管理并发任务的执行
	 * 
	 * 该线程池通过固定数量的工作线程来处理任务队列中的任务，支持动态任务添加。
	 * 使用者通过 `enqueue` 方法提交任务，线程池的工作线程会从任务队列中取出任务并执行。
	 * 线程池会在析构时自动清理所有线程。
	 * 
	 * @tparam MaxThreads 最大线程数，默认为 10
	 */
	// 先给最大线程数做一个限制
	template <size_t MaxThread = 10>
	class ThreadPool
	{
	private:
		std::vector<std::thread> workers;				// 工作线程列表  每个线程从任务队列中pop出来执行任务
		std::queue<std::function<void()>> tasks;		// 任务队列
		std::mutex queue_mutex;							// 保护任务队列的互斥锁
		std::condition_variable condition;				// 线程同步的条件变量 保证能等待任务
		bool stop;										// 标志量
	public:
		/**
    	 * @brief 构造函数，初始化线程池并启动工作线程
    	 * 
    	 * 在构造函数中，线程池会启动固定数量的工作线程，每个线程会循环等待任务队列中的任务，
    	 * 一旦队列中有任务，工作线程会取出任务并执行。
    	 * 构造时会检查最大线程数是否为正值。
    	 */
		// 构造 初始化
		// 默认不停止工作
		ThreadPool() : stop(false) {
			// 线程数有效性检查
        	static_assert(MaxThread > 0, "Thread count must be positive");
			for (size_t i = 0; i < MaxThread; i++)
			{
				workers.emplace_back([this] -> void {
					while (true) {
						// 当前要执行的任务 存储一下
						std::function<void()> task;
						// 上锁要特别注意生命周期
						{
							std::unique_lock<std::mutex> lock(queue_mutex);		// 上锁
							// 等一下 有任务或者线程池需要时 停止
							condition.wait(lock, [this] -> bool {
								return stop || !tasks.empty();
							});
							// 线程池停止 AND 没有任务 循环就break
							if (stop && tasks.empty())	return;
							// 任务队列 取任务
							task = std::move(tasks.front());
							tasks.pop();
						}
						task();		// 执行任务
					}
				});
			}
		}

		/**
    	 * @brief 向线程池提交任务
    	 * 
    	 * @tparam F 函数对象类型
    	 * @param f 要执行的任务
    	 * 
    	 * 将任务添加到任务队列中，并通知一个线程执行该任务。禁止在线程池停止后添加任务。
    	 */
		// push 进任务队列
		template<class F>
		void enqueue(F&& f) {
			// 上锁要特别注意生命周期
			{
				std::unique_lock<std::mutex> lock(queue_mutex);		// 上锁
				// 禁止在停止后添加任务
            	if (stop) {
            	    throw std::runtime_error("enqueue on stopped ThreadPool");
            	}
				// 进任务队列
				tasks.emplace(std::forward<F>(f));
			}
			// 通知一个线程之后就 执行任务
			condition.notify_one();
		}

		/**
    	 * @brief 析构函数，停止线程池并清理工作线程
    	 * 
    	 * 在析构时，线程池会设置停止标志并通知所有工作线程退出。然后会等待所有线程执行完毕并退出。
    	 */
		~ThreadPool() {
			{
				std::unique_lock<std::mutex> lock(queue_mutex);		// 上锁
				stop = true;	// 析构之后就 线程池停止
			}
			// 唤醒全部线程
			condition.notify_all();
			// 等待线程结束
			for (auto& worker : workers) {
				if (worker.joinable()) {
					worker.join();		// 等待执行完毕
				}
			}
		}

		// 禁止移动和拷贝
		ThreadPool(const ThreadPool&) = delete;
		ThreadPool& operator=(const ThreadPool&) = delete;

	};
}